package config

import (
	"fmt"
	"github.com/garyburd/redigo/redis"
	"google.golang.org/grpc"
	"io/ioutil"
	"os"
	"strings"
	"zimuzu_cvn_web_api/pkg/drive/db"

	"github.com/fsnotify/fsnotify"
	"github.com/gin-gonic/gin"
	log "github.com/sirupsen/logrus"
	"github.com/spf13/viper"
)

// Version defines the LoRa Server version.
var Version string

// C holds the global configuration.
var C Config

// Config defines the configuration structure.
type Config struct {
	Name            string
	RunMode         string `mapstructure:"runmode"`
	SVer            string `mapstructure:"sver"`
	Address         string `mapstructure:"addr"`
	YpApiKey        string `mapstructure:"yunpian_apikey"`
	FlowKey         string `mapstructure:"flow_key"`
	LxUserName      string `mapstructure:"lx_username"`
	LxPassword      string `mapstructure:"lx_password"`
	YyetsV3Key      string `mapstructure:"yyets_v3_key"`
	JwtSecret       string `mapstructure:"jwt_secret"`
	SessionSecret   string `mapstructure:"sessionSecret"`
	UnlockDate      int    `mapstructure:"unlockDate"`
	ShopDomain      string `mapstructure:"shop_domain"`
	JapenUrl        string `mapstructure:"japen_url"`
	TaskWallDomain  string `mapstructure:"task_wall_domain"`
	WalletH5Domain  string `mapstructure:"wallet_h5_domain"`
	TodayCvntDomain string `mapstructure:"today_cvnt_domain"`
	AppVersion      int    `mapstructure:"appVersion"`
	ApiKey          string `mapstructure:"api_key"`
	PledgeMaxNum    int    `mapstructure:"pledgeMaxNum"`

	Domains struct {
		Ftp            string `mapstructure:"ftp"`
		YqbAppMenu     string `mapstructure:"yqbAppMenu"`
		PaymentGateway string `mapstructure:"paymentGateway"`
		ResourcePic    string `mapstructure:"resourcePic"`
		ResourceExcel  string `mapstructure:"resourceExcel"`
	} `mapstructure:"domains"`

	Keys struct {
		AppAccessKey string `mapstructure:"appAccessKey"`
		WebAccessKey string `mapstructure:"webAccessKey"`
		TransferKey  string `mapstructure:"transferKey"`
		GatewayKey   string `mapstructure:"gatewayKey"`
	} `mapstructure:"keys"`

	Log struct {
		LoggerLevel int    `mapstructure:"logger_level"`
		LoggerFile  string `mapstructure:"logger_file"`
		LoggerType  string `mapstructure:"logger_type"`
	} `mapstructure:"log"`

	DB struct {
		Type string `mapstructure:"type"`
		Name string `mapstructure:"name"`
		DSN  string `mapstructure:"dsn"`
		DB   *db.DBLogger
	} `mapstructure:"db"`

	Redis struct {
		URL       string `mapstructure:"url"`
		Password  string `mapstructure:"password`
		RedisPool *redis.Pool
	} `mapstructure:"redis"`

	Email struct {
		Host     string `mapstructure:"host"`
		User     string `mapstructure:"user"`
		Password string `mapstructure:"password"`
		Port     int    `mapstructure:"port"`
	}

	Geetest struct {
		CaptchId   string `mapstructure:"captchId"`
		PrivateKey string `mapstructure:"privateKey"`
	}

	Rpc struct {
		Url  string `mapstructure:"url"`
		Conn *grpc.ClientConn
	} `mapstructure:"rpc"`

	LogId struct {
		Cvnt int64 `mapstructure:"cvnt"`
		Usdt int64 `mapstructure:"usdt"`
	} `mapstructure:"log_id"`

	Router *gin.Engine
}

func Init(cfg string) error {

	c := Config{
		Name: cfg,
	}

	// 初始化配置文件
	if err := c.InitConfig(); err != nil {
		fmt.Fprintf(os.Stderr, "init config error: %s\n", err.Error())
		return err
	}

	// 监控配置文件变化并热加载程序
	c.watchConfig()

	return nil
}

func (c *Config) InitConfig() error {
	if c.Name != "" {
		viper.SetConfigFile(c.Name) // 如果指定了配置文件，则解析指定的配置文件
	} else {
		viper.AddConfigPath("conf") // 如果没有指定配置文件，则解析默认的配置文件
		env, err := ioutil.ReadFile("conf/env.txt")
		if err != nil {
			return err
		}
		if strings.Contains(string(env), "pro") {
			viper.SetConfigName("config")
		} else {
			viper.SetConfigName("config_dev")
		}
	}
	viper.SetConfigType("yaml")         // 设置配置文件格式为YAML
	viper.AutomaticEnv()                // 读取匹配的环境变量
	viper.SetEnvPrefix("web_framework") // 读取环境变量的前缀为APISERVER
	replacer := strings.NewReplacer(".", "_")
	viper.SetEnvKeyReplacer(replacer)
	if err := viper.ReadInConfig(); err != nil { // viper解析配置文件
		return err
	} else {
		fmt.Printf("init using config file %s\n", viper.ConfigFileUsed())
	}

	if err := viper.Unmarshal(&C); err != nil {
		return err
	}

	return nil
}

// 监控配置文件变化并热加载程序
func (c *Config) watchConfig() {
	viper.WatchConfig()
	viper.OnConfigChange(func(e fsnotify.Event) {
		log.Infof("Config file changed: %s", e.Name)
	})
}
